package cn.buk.tms.eotms.dao;

import cn.buk.common.sc.CommonSearchCriteria;
import cn.buk.common.sc.Page;
import cn.buk.common.util.DateUtil;
import cn.buk.tms.eotms.entity.*;
import cn.buk.tms.eotms.sc.SearchFlightOrderCriteria;
import org.springframework.stereotype.Component;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;

/**
 * @author yfdai
 */
@Component
@Transactional
public class EnterpriseDaoImpl extends AbstractDao implements EnterpriseDao {

  @Override
  public List<Passenger> searchPassengers(final int ownerId, final int customerId, SearchFlightOrderCriteria sc) {
    List<Passenger> results = null;

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Passenger> cq = cb.createQuery(Passenger.class);
    Root<Passenger> root = cq.from(Passenger.class);
    root.alias("o");

    var predicates0 = getPredicates_searchPassengers(ownerId, customerId, sc, cb, root);


    //计算根据条件查询得出的数据总数
    CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
    Root<Passenger> countRoot = countQuery.from(Passenger.class);
    countRoot.alias("o");

    var predicates1 = getPredicates_searchPassengers(ownerId, customerId, sc, cb, countRoot);

    // count items
    countQuery.select(cb.count(countRoot))
        .where(predicates1.toArray(new Predicate[predicates1.size()]));
    Long count = em.createQuery(countQuery).getSingleResult();

    //查询
    cq.select(root).where(predicates0.toArray(new Predicate[predicates0.size()]));

    List<jakarta.persistence.criteria.Order> orderByes = new ArrayList<>();
    orderByes.add(cb.asc(root.get("nameEn")));

    cq.orderBy(orderByes);

    int maxResults = count.intValue();
    if (maxResults > 0) {
      Page page = sc.getPage();
      page.setRowCount(maxResults);
      try {
        results = em.createQuery(cq)
            .setFirstResult(page.getFirstPosition())
            .setMaxResults(page.getPageSize()).getResultList();

      } catch (Exception ex) {
        ex.printStackTrace();
      }
    }

    if (results == null) {
      results = new ArrayList<>();
    }

    return results;
  }

  private static List<Predicate> getPredicates_searchPassengers(int ownerId, int customerId, SearchFlightOrderCriteria sc, CriteriaBuilder cb, Root<Passenger> root) {
    List<Predicate> predicates = new ArrayList<>();

    Predicate predicate = cb.conjunction();
    predicates.add(predicate);

    predicate = cb.equal(root.get(Passenger_.enterpriseId), ownerId);
    predicates.add(predicate);

    predicate = cb.equal(root.get(Passenger_.customerId), customerId);
    predicates.add(predicate);

    if (sc.getDepartmentId() != null && sc.getDepartmentId() > 0) {
      predicate = cb.equal(root.get(Passenger_.department).get(CustomerDepartment_.id), sc.getDepartmentId());
      predicates.add(predicate);
    }

    if (sc.getPassengerId() != null) {
      predicate = cb.equal(root.get(Passenger_.id), sc.getPassengerId());
      predicates.add(predicate);
    }

    if (sc.getName() != null && sc.getName().trim().length() > 0) {
      predicate = cb.like(root.get(Passenger_.nameCn), "%" + sc.getName() + "%");
      predicate = cb.or(predicate, cb.like(root.get(Passenger_.nameEn), "%" + sc.getName() + "%"));
      predicates.add(predicate);
    }

    //TODO 需要使用PassengerIdNo表中的证件号进行查询
    if (sc.getIdno() != null && sc.getIdno().trim().length() > 0) {
      predicate = cb.like(root.get(Passenger_.idNo), "%" + sc.getIdno() + "%");
      predicates.add(predicate);
    }
    return predicates;
  }

  @Override
  public List<CustomerDepartment> searchDepartments(int ownerId, int customerId, CommonSearchCriteria sc) {
    List<CustomerDepartment> results = null;

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<CustomerDepartment> cq = cb.createQuery(CustomerDepartment.class);
    Root<CustomerDepartment> root = cq.from(CustomerDepartment.class);
    root.alias("o");

    var predicates0 = getPredicates_searchDepartments(ownerId, customerId, sc, cb, root);

    //计算根据条件查询得出的数据总数
    CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
    Root<CustomerDepartment> countRoot = countQuery.from(CustomerDepartment.class);
    countRoot.alias("o");

    var predicates1 = getPredicates_searchDepartments(ownerId, customerId, sc, cb, countRoot);

    // count items
    countQuery.select(cb.count(countRoot))
            .where(predicates1.toArray(new Predicate[predicates1.size()]));
    Long count = em.createQuery(countQuery).getSingleResult();

    //查询
    cq.select(root).where(predicates0.toArray(new Predicate[predicates0.size()]));

    List<jakarta.persistence.criteria.Order> orderByes = new ArrayList<>();
    orderByes.add(cb.desc(root.get("name")));

    cq.orderBy(orderByes);

    int maxResults = count.intValue();
    if (maxResults > 0) {
      Page page = sc.getPage();
      page.setRowCount(maxResults);

      try {
        results = em.createQuery(cq)
                .setFirstResult(page.getFirstPosition())
                .setMaxResults(page.getPageSize())
                .getResultList();

      } catch (Exception ex) {
        ex.printStackTrace();
      }
    }

    if (results == null) {
      results = new ArrayList<>();
    }

    return results;
  }

  private static List<Predicate> getPredicates_searchDepartments(int ownerId, int customerId, CommonSearchCriteria sc, CriteriaBuilder cb, Root<CustomerDepartment> root) {
    List<Predicate> predicates = new ArrayList<>();

    Predicate predicate = cb.conjunction();
    predicates.add(predicate);

    predicate = cb.equal(root.get(CustomerDepartment_.enterpriseId), ownerId);
    predicates.add(predicate);

    predicate = cb.equal(root.get(CustomerDepartment_.customerId), customerId);
    predicates.add(predicate);


    if (sc.getName() != null && sc.getName().trim().length() > 0) {
      predicate = cb.like(root.get(CustomerDepartment_.name), "%" + sc.getName() + "%");
      predicates.add(predicate);
    }
    return predicates;
  }

  @Override
  public int createOrUpdateDepartment(int ownerId, CustomerDepartment dto) {
    int retCode = 0;

    dto.setEnterpriseId(ownerId);

    if (dto.getId() == 0) {

      em.persist(dto);

      retCode = dto.getId();
    } else {
      dto.setLastUpdate(DateUtil.getCurDateTime());

      em.merge(dto);

      retCode = 1;
    }

    return retCode;
  }

  @Override
  public Passenger getPassengerById(int ownerId, int customerId, int id) {
    Passenger passenger = em.find(Passenger.class, id);
    if (passenger == null) {
      return null;
    }

    if (passenger.getEnterpriseId() != ownerId || passenger.getCustomer() == null || passenger.getCustomer().getId() != customerId) {
      return null;
    } else {
      return passenger;
    }
  }

  @Override
  public Passenger getPassengerById(int id) {
    return em.find(Passenger.class, id);
  }

  @Override
  public AppIdInfo searchAppId(String appId) {
    List<AppIdInfo> idInfos = em.createQuery("select o from AppIdInfo o where o.appId = :appId", AppIdInfo.class)
            .setParameter("appId", appId)
            .getResultList();

    return idInfos.size() == 0 ? null : idInfos.get(0);
  }

  @Override
  public String getEnterpriseConfig(int enterpriseId, String itemCode) {
    return doGetItemValue(enterpriseId, itemCode);
  }

  @Override
  public List<ProductDefaultSupplier> searchApiProductSupplier(int ownerId) {
    return em.createQuery("select o from ProductDefaultSupplier o " +
            "where o.enterpriseId = :enterpriseId order by o.productType", ProductDefaultSupplier.class)
            .setParameter("enterpriseId", ownerId)
            .getResultList();
  }

  @Override
  public List<VehicleGroupMapping> searchVehicleGroupMappings(int ownerId, int supplierId) {
    return em.createQuery("select o from VehicleGroupMapping o " +
            "where o.enterpriseId = :enterpriseId and o.supplier.id = :supplierId", VehicleGroupMapping.class)
            .setParameter("enterpriseId", ownerId)
            .setParameter("supplierId", supplierId)
            .getResultList();
  }

  @Override
  public List<CustomerTravelApprover> searchCustomerApprovers(int ownerId, int customerId) {
    return em.createQuery("select o from CustomerTravelApprover o " +
            "where o.enterpriseId = : enterpriseId " +
            "and o.customer.id = :customerId ", CustomerTravelApprover.class)
            .setParameter("enterpriseId", ownerId)
            .setParameter("customerId", customerId)
            .getResultList();
  }

  @Override
  public AuthCodeInfo getAuthCodeInfo(String authCode) {
    List<AuthCodeInfo> cfgs = em
            .createQuery("select o from AuthCodeInfo o where o.authCode = :authCode",
                    AuthCodeInfo.class)
            .setParameter("authCode", authCode)
            .getResultList();
    if (cfgs.isEmpty()) {
      return null;
    } else {
      AuthCodeInfo info = cfgs.get(0);
      info.setCount(info.getCount()+1);
      info.setLastUpdate(DateUtil.getCurDateTime());
      em.merge(info);

      return info;
    }
  }

  @Override
  public CustomerServiceTemplate searchServiceTemplateByCustomer(final int ownerId, final int customerId) {
    Customer c = em.find(Customer.class, customerId);
    if (c == null || c.getEnterpriseId() != ownerId) {
      return null;
    }

    if (c.getTemplateCode() == null || c.getTemplateCode().trim().length() == 0) {
      return null;
    }

    List<CustomerServiceTemplate> tpls = em.createQuery("select o from CustomerServiceTemplate o " +
            "where o.enterpriseId = :enterpriseId and o.templateCode = :code", CustomerServiceTemplate.class)
            .setParameter("enterpriseId", ownerId)
            .setParameter("code", c.getTemplateCode())
            .getResultList();

    return tpls.size() > 0 ? tpls.get(0): null;
  }

  @Override
  public EnterpriseBalance getEnterpriseBalance(int ownerId, int enterpriseId) {
    return doGetEnterpriseBalance(ownerId, enterpriseId);
  }

  private String doGetItemValue(int enterpriseId, String itemCode) {
    String result = "";

    List<EnterpriseConfig> configs = em.createQuery("select o from EnterpriseConfig o " +
            "where o.enterpriseId = :enterpriseId and o.itemCode = :itemCode", EnterpriseConfig.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("itemCode", itemCode)
            .getResultList();

    if (configs.size() == 1) {
      result = configs.get(0).getItemValue().trim();
    }

    return result;
  }
}
